package com.mediaideas.videoengine.recorder;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.mediaideas.videoengine.utils.FileUtils;

import java.io.File;
import java.io.IOException;

public class AudioRecorder {

    private static final String TAG = "AudioRecorder";
    private final String mTargetRecordFileName;
    private final Context mContext;
    private final MediaRecorderConfig mMediaRecorderConfig;
    private final boolean mIsLoggable;
    private Status mStatus;
    private MediaRecorder mMediaRecorder;

    AudioRecorder(@NonNull final Context context,
                  @NonNull final String targetRecordFileName,
                  @NonNull final MediaRecorderConfig mediaRecorderConfig,
                  final boolean isLoggable) {
        mTargetRecordFileName = targetRecordFileName;
        mContext = context;
        mMediaRecorderConfig = mediaRecorderConfig;
        mIsLoggable = isLoggable;

        mStatus = Status.STATUS_READY_TO_RECORD;
    }

    /**
     * Returns a ready-to-use AudioRecorder.
     * Uses {@link AudioRecorder.MediaRecorderConfig#DEFAULT} as
     * {@link android.media.MediaRecorder} config.
     *
     * @deprecated Use AudioRecorderBuilder instead.
     */
    public static AudioRecorder build(@NonNull final Context context,
                                      @NonNull final String targetFileName) {
        return build(context, targetFileName, MediaRecorderConfig.DEFAULT);
    }

    /**
     * Returns a ready-to-use AudioRecorder.
     *
     * @deprecated Use AudioRecorderBuilder instead.
     */
    public static AudioRecorder build(@NonNull final Context context,
                                      @NonNull final String targetFileName,
                                      @NonNull final MediaRecorderConfig mediaRecorderConfig) {
        AudioRecorder rvalue = new AudioRecorder(
                context,
                targetFileName,
                mediaRecorderConfig,
                false);
        rvalue.mStatus = Status.STATUS_READY_TO_RECORD;
        return rvalue;
    }

    /**
     * Continues an existing record or starts a new one.
     */
    @SuppressLint("NewApi")
    public void start(@NonNull final OnStartListener listener) {
        StartRecordTask task = new StartRecordTask();
        task.execute(listener);

    }

    /**
     * Pauses an active recording.
     */
    @SuppressLint("NewApi")
    public void pause(@NonNull final OnPauseListener listener) {
        PauseRecordTask task = new PauseRecordTask();
        task.execute(listener);
    }

    /**
     * 停止录制
     *
     * @param listener
     */
    public void stop(@NonNull final OnStopListener listener) {
        StopRecordTask stopRecordTask = new StopRecordTask();
        stopRecordTask.execute(listener);
    }

    /**
     * Returns the current recording status.
     *
     * @see AudioRecorder
     */
    public Status getStatus() {
        return mStatus;
    }

    private void setStatus(@NonNull final Status status) {
        mStatus = status;
    }

    /**
     * Returns the current record filename.
     */
    public String getRecordFileName() {
        return mTargetRecordFileName != null ? mTargetRecordFileName : getTemporaryFileName();
    }

    /**
     * Returns true if record is started, false if not.
     */
    public boolean isRecording() {
        return mStatus == Status.STATUS_RECORDING;
    }

    /**
     * Returns true if record can be started, false if not.
     */
    public boolean isReady() {
        return mStatus == Status.STATUS_READY_TO_RECORD;
    }

    /**
     * Returns true if record is paused, false if not.
     */
    public boolean isPaused() {
        return mStatus == Status.STATUS_RECORD_PAUSED;
    }

    /**
     * /data/data/com.hangzhou.xyj/tmprecord.acc
     *
     * @return
     */
    public String getTemporaryFileName() {
        return mContext.getCacheDir().getAbsolutePath() + File.separator + "tmprecord.aac";
    }

    /**
     * /data/data/com.hangzhou.xyj/cache/record_temp/xxxx_tmp.aac
     * 获取随机缓存文件的名称，没有文件夹 则创建
     *
     * @return
     */
    public String getRandomTempFileName() {
        String tmpDir = mContext.getCacheDir().getAbsolutePath() + File.separator + "record_tmp";
        if (!FileUtils.isFileExists(tmpDir)) {
            FileUtils.mkdir(tmpDir);
        }
        return tmpDir + File.separator + System.currentTimeMillis() + "_tmp.aac";
    }

    public String getRandomTempDir() {
        return mContext.getCacheDir().getAbsolutePath() + File.separator + "record_tmp";
    }

    /**
     * 删除临时缓存文件夹
     *
     * @return
     */
    public boolean deleteTempRecordDir() {
        String tmpDir = mContext.getCacheDir().getAbsolutePath() + File.separator + "record_tmp";
        return FileUtils.deleteDir(tmpDir);
    }

    /**
     * 获取临时缓存文件夹 路径
     *
     * @return
     */
    public String getTempRecordDirName() {
        return mContext.getCacheDir().getAbsolutePath() + File.separator + "record_tmp";
    }

    /**
     * 拼接newFileName到targetFileName中（video/audio）
     *
     * @param targetFileName
     * @param newFileName
     */
    private void appendToFile(@NonNull final String targetFileName, @NonNull final String newFileName) {
        Mp4ParserWrapper.append(targetFileName, newFileName);
    }

    /**
     * 取消录制，变成 未知状态
     * Drops the current recording.
     */
    public void cancel() {
        try {
            if (mMediaRecorder != null) {
                mMediaRecorder.stop();
                mMediaRecorder.release();
            }
        } catch (Exception e) {
            error("Exception during record cancelling", e);
        }
        mStatus = Status.STATUS_UNKNOWN;
    }

    private void debug(@NonNull final String msg, @Nullable final Exception e) {
        if (mIsLoggable) Log.d(TAG, msg, e);
    }

    private void error(@NonNull final String msg, @Nullable final Exception e) {
        if (mIsLoggable) Log.e(TAG, msg, e);
    }

    public enum Status {
        STATUS_UNKNOWN,//未知
        STATUS_READY_TO_RECORD,//准备录制
        STATUS_RECORDING,//正在录制
        STATUS_RECORD_PAUSED//暂停录制
    }

    public interface OnException {
        void onException(Exception e);
    }

    public interface OnStartListener extends OnException {
        void onStarted();
    }

    public interface OnPauseListener extends OnException {
        void onPaused(String activeRecordFileName);
    }

    public interface OnStopListener extends OnException {
        void onStop(String activeRecordFileName);
    }

    public interface OnRestartListener extends OnException {
        void onRestart();
    }

    /**
     * 录音配置:编码码率,通道数(立体声2通道),编码器-目前使用AAC编码,音频源使用默认值
     */
    public static class MediaRecorderConfig {
        public static final MediaRecorderConfig DEFAULT =
                new MediaRecorderConfig(
                        /* 128 Kib per second            */
                        128 * 1024,
                        /* Stereo                       */
                        2,
                        /* Default audio source (usually, device microphone)  */
                        MediaRecorder.AudioSource.DEFAULT,
                        /* Default encoder for the target Android version   */
                        ApiHelper.DEFAULT_AUDIO_ENCODER);
        private final int mAudioEncodingBitRate;
        private final int mAudioChannels;
        private final int mAudioSource;
        private final int mAudioEncoder;

        /**
         * Constructor.
         *
         * @param audioEncodingBitRate Used for {@link android.media.MediaRecorder#setAudioEncodingBitRate}
         * @param audioChannels        Used for {@link android.media.MediaRecorder#setAudioChannels}
         * @param audioSource          Used for {@link android.media.MediaRecorder#setAudioSource}
         * @param audioEncoder         Used for {@link android.media.MediaRecorder#setAudioEncoder}
         */
        public MediaRecorderConfig(int audioEncodingBitRate, int audioChannels, int audioSource, int audioEncoder) {
            mAudioEncodingBitRate = audioEncodingBitRate;
            mAudioChannels = audioChannels;
            mAudioSource = audioSource;
            mAudioEncoder = audioEncoder;
        }

    }

    /**
     * 使用异步任务启动,规避一些问题
     * 开始录制 异步任务
     */
    class StartRecordTask extends AsyncTask<OnStartListener, Void, Exception> {

        private OnStartListener mOnStartListener;

        @Override
        protected Exception doInBackground(OnStartListener... params) {
//            FileUtils.deleteFile(getTemporaryFileName());//删除临时文件
            this.mOnStartListener = params[0];
            mMediaRecorder = new MediaRecorder();
            // 码率设置目前设置未96kbit/s
            mMediaRecorder.setAudioEncodingBitRate(mMediaRecorderConfig.mAudioEncodingBitRate);
            // 通道设置-2
            mMediaRecorder.setAudioChannels(mMediaRecorderConfig.mAudioChannels);
            // 音频源-一般采用麦克风,目前使用默认
            mMediaRecorder.setAudioSource(mMediaRecorderConfig.mAudioSource);
            // 音频输出格式-AAC
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS/*MPEG_4*/);
            // 音频输出路径
            mMediaRecorder.setOutputFile(getRandomTempFileName());
            // 音频编码器设置-AAC
            mMediaRecorder.setAudioEncoder(mMediaRecorderConfig.mAudioEncoder);

            Exception exception = null;
            try {
                mMediaRecorder.prepare();
                mMediaRecorder.start();
            } catch (IOException e) {
                exception = e;
                e.printStackTrace();
            }
            return exception;
        }

        @Override
        protected void onPostExecute(Exception e) {
            super.onPostExecute(e);
            if (e == null) {
                setStatus(AudioRecorder.Status.STATUS_RECORDING);//设置状态为 正在录制
                mOnStartListener.onStarted();//回调
            } else {
                setStatus(AudioRecorder.Status.STATUS_READY_TO_RECORD);//设置状态为准备录制
                mOnStartListener.onException(e);
            }
        }
    }

    /**
     * 暂停录制 异步任务
     */
    class PauseRecordTask extends AsyncTask<OnPauseListener, Void, Exception> {
        private OnPauseListener mOnPauseListener;

        @Override
        protected Exception doInBackground(OnPauseListener... params) {
            mOnPauseListener = params[0];
            Exception exception = null;
            try {
                mMediaRecorder.stop();
                mMediaRecorder.release();
            } catch (Exception e) {
                exception = e;
            }
//            if (exception == null) {
//                //放到cache文件夹中
//                appendToFile(getTemporaryFileName(), mTargetRecordFileName);
//            }
            return exception;
        }

        @Override
        protected void onPostExecute(Exception e) {
            super.onPostExecute(e);
            if (e == null) {
                setStatus(AudioRecorder.Status.STATUS_RECORD_PAUSED);
                mOnPauseListener.onPaused(mTargetRecordFileName);
            } else {
                setStatus(AudioRecorder.Status.STATUS_READY_TO_RECORD);
                mOnPauseListener.onException(e);
            }
        }
    }

    /**
     * 停止录制异步任务
     */
    private class StopRecordTask extends AsyncTask<OnStopListener, Void, Exception> {
        OnStopListener mOnStopListener;

        @Override
        protected Exception doInBackground(OnStopListener... params) {
            mOnStopListener = params[0];
            Exception exception = null;
            try {
                if (mStatus == AudioRecorder.Status.STATUS_RECORDING) {
                    mMediaRecorder.stop();
                    mMediaRecorder.release();
                }
            } catch (Exception e) {
                exception = e;
            }
            if (exception == null) {//没有异常
                try {
                    Mp4ParserWrapper.append(mTargetRecordFileName, FileUtils.getFileFromDir(getRandomTempDir()));
                    deleteTempRecordDir();//删除临时目录 及其子文件
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return exception;
        }

        @Override
        protected void onPostExecute(Exception e) {
            if (e == null) {//没有发生异常
                setStatus(AudioRecorder.Status.STATUS_UNKNOWN);
                mOnStopListener.onStop(mTargetRecordFileName);
            } else {
                setStatus(AudioRecorder.Status.STATUS_READY_TO_RECORD);
                mOnStopListener.onException(e);
            }
        }
    }

}
